/*
 * Copyright (c) 2023 Huawei Technologies Co., Ltd.
 * openUBMC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 *         http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 */

#include "pca9555.h"
#include <mc/exception.h>
#include <mc/log.h>

using namespace dev;

bool chip_pca9555::init(mc::mutable_dict& csr_object) {
    try {
        // 调用chip_base的init函数
        chip_base::init(csr_object);
    } catch (const std::exception& e) {
        elog("PCA9555 configuration loading failed: ${error}", ("error", e.what()));
        return false;
    }

    return true;
}

bool check_para(const std::vector<uint8_t>& in_buffer, uint32_t offset, uint8_t length,
                uint32_t mask) {
    if (in_buffer.empty()) {
        MC_THROW(mc::invalid_op_exception, "in_buffer is empty");
    }
    if (mask == 0) {
        MC_THROW(mc::invalid_op_exception, "invalid mask: 0");
    }
    if (length != 1) {
        MC_THROW(mc::invalid_op_exception, "invalid length: ${len}", ("len", length));
    }
    if (offset > 1) {
        MC_THROW(mc::invalid_op_exception, "invalid offset: ${offset}", ("offset", offset));
    }
    return true;
}

int32_t chip_pca9555::bit_write(uint32_t offset, uint8_t length, uint32_t mask,
                                 const std::vector<uint8_t>& in_buffer,
                                 std::vector<uint8_t>&       out_buffer, std::string& error) {
    // 检查前级总线是否存在
    if (!m_host_bus || !m_left_bus) {
        error = "Previous bus object not found";
        MC_THROW(mc::invalid_op_exception, error);
    }

    // 参数验证
    check_para(in_buffer, offset, length, mask);
    
    try {
        // 确定寄存器地址
        uint32_t config_reg = (offset == 0) ? IO0_CONFIG_REG : IO1_CONFIG_REG;
        uint32_t output_reg = (offset == 0) ? IO0_OUTPUT_REG : IO1_OUTPUT_REG;

        // 临时缓冲区
        std::vector<uint8_t> tmp_in_buffer(1, 0);
        std::vector<uint8_t> tmp_out_buffer(1, 0);
        int32_t              ret           = HAS_OK;
        uint8_t              current_value = 0;
        std::string          read_error, write_error;

        // 1. 配置寄存器操作：将mask对应的位设置为输出模式
        ret = chip_base::bit_read(config_reg, 1, 0xFF, tmp_in_buffer, tmp_out_buffer, read_error);
        if (ret != HAS_OK) {
            error = "Failed to read configuration register: offset=" + std::to_string(offset) + ", ret=" + std::to_string(ret) + ", error=" + read_error;
            ilog("${error}", ("error", error));
            return ret;
        }

        // 清除mask对应的位（设置为输出模式）
        current_value    = tmp_out_buffer[0] & (~mask);
        tmp_in_buffer[0] = current_value;

        ret = chip_base::bit_write(config_reg, 1, 0xFF, tmp_in_buffer, tmp_out_buffer, write_error);
        if (ret != HAS_OK) {
            error = "Failed to write configuration register: offset=" + std::to_string(offset) + ", ret=" + std::to_string(ret) + ", error=" + write_error;
            ilog("${error}", ("error", error));
            return ret;
        }

        // 2. 输出寄存器操作：写入数据到指定位
        ret = chip_base::bit_read(output_reg, 1, 0xFF, tmp_in_buffer, tmp_out_buffer, read_error);
        if (ret != HAS_OK) {
            error = "Failed to read output register: offset=" + std::to_string(offset) + ", ret=" + std::to_string(ret) + ", error=" + read_error;
            ilog("${error}", ("error", error));
            return ret;
        }

        // 计算要写入的值：保持非mask位不变，mask位使用输入数据
        uint8_t input_value = in_buffer[0];
        current_value       = tmp_out_buffer[0];

        // 找到mask中第一个1的位置，用于对齐输入数据
        uint32_t first_bit_pos = 0;
        for (uint8_t i = 0; i < 8; ++i) {
            if ((mask >> i) & 0x01) {
                first_bit_pos = i;
                break;
            }
        }

        // 将输入数据对齐到正确位置，并与当前值合并
        uint8_t aligned_input = (input_value << first_bit_pos) & mask;
        uint8_t new_value     = (current_value & (~mask)) | aligned_input;

        tmp_in_buffer[0] = new_value;
        ret              = chip_base::bit_write(output_reg, 1, mask, tmp_in_buffer, tmp_out_buffer, write_error);
        if (ret != HAS_OK) {
            error = "Failed to write output register: offset=" + std::to_string(offset) + ", ret=" + std::to_string(ret) + ", error=" + write_error;
            ilog("${error}", ("error", error));
            return ret;
        }

        return HAS_OK;
    } catch (const std::exception& e) {
        error = std::string("bit_write exception: ") + e.what();
        ilog("bit_write exception: ${error}", ("error", e.what()));
        return HAS_ERR;
    }
}